package org.activiti.engine.impl.history;

import com.alibaba.fastjson.JSONObject;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.delegate.event.ActivitiEventDispatcher;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.cfg.IdGenerator;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.engine.impl.history.HistoryManager;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.AbstractManager;
import org.activiti.engine.impl.persistence.cache.EntityCache;
import org.activiti.engine.impl.persistence.entity.CommentEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntity;
import org.activiti.engine.impl.persistence.entity.HistoricDetailVariableInstanceUpdateEntity;
import org.activiti.engine.impl.persistence.entity.HistoricIdentityLinkEntity;
import org.activiti.engine.impl.persistence.entity.HistoricProcessInstanceEntity;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.activiti.engine.impl.persistence.entity.HistoricVariableInstanceEntity;
import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.springframework.stereotype.Component;

/**
 * @author dingxian
 * @Title:
 * @param:
 * @return:
 * @Date 2023/3/4 17:06
 * @Description:
 */
@Component
@Slf4j
public class MyDefaultHistoryManager extends AbstractManager implements HistoryManager {




  //记录的水平
  private HistoryLevel historyLevel;

  public MyDefaultHistoryManager() {
    
    super(null);
    this.historyLevel=HistoryLevel.NONE;


  };

  public MyDefaultHistoryManager(
      ProcessEngineConfigurationImpl processEngineConfiguration) {
    super(processEngineConfiguration);
  }

  public MyDefaultHistoryManager(ProcessEngineConfigurationImpl processEngineConfiguration,
      HistoryLevel historyLevel) {
    super(processEngineConfiguration);
    this.historyLevel = historyLevel;
  }

  /**
   * 判断HistoryLevel 是否符合最低输出需求
   *
   * @param level
   * @return
   */
  public boolean isHistoryLevelAtLeast(HistoryLevel level) {
    if (log.isDebugEnabled()) {
      log.debug("Current history level: {}, level required: {}", this.historyLevel, level);
    }

    return this.historyLevel.isAtLeast(level);
  }

  public boolean isHistoryEnabled() {
    if (log.isDebugEnabled()) {
      log.debug("Current history level: {}", this.historyLevel);
    }

    return !this.historyLevel.equals(HistoryLevel.NONE);
  }

  public void recordProcessInstanceEnd(String processInstanceId, String deleteReason,
      String activityId) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricProcessInstanceEntity historicProcessInstance = (HistoricProcessInstanceEntity) this.getHistoricProcessInstanceEntityManager()
          .findById(processInstanceId);
      if (historicProcessInstance != null) {
        historicProcessInstance.markEnded(deleteReason);
        historicProcessInstance.setEndActivityId(activityId);
        ActivitiEventDispatcher activitiEventDispatcher = this.getEventDispatcher();
        if (activitiEventDispatcher != null && activitiEventDispatcher.isEnabled()) {
          activitiEventDispatcher.dispatchEvent(
              ActivitiEventBuilder.createEntityEvent(
                  ActivitiEventType.HISTORIC_PROCESS_INSTANCE_ENDED, historicProcessInstance));
        }
      }
    }

  }

  public void recordProcessInstanceNameChange(String processInstanceId, String newName) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricProcessInstanceEntity historicProcessInstance = (HistoricProcessInstanceEntity) this.getHistoricProcessInstanceEntityManager()
          .findById(processInstanceId);
      if (historicProcessInstance != null) {
        historicProcessInstance.setName(newName);
      }
    }

  }

  /**
   * @param processInstance 执行的实例
   * @param startElement    开始的流程元素
   */
  public void recordProcessInstanceStart(ExecutionEntity processInstance,
      FlowElement startElement) {
    //检查数据记录等级
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      //创建 历史流程实例 实体管理器  对传入的实例进行操作
      HistoricProcessInstanceEntity historicProcessInstance = this.getHistoricProcessInstanceEntityManager()
          .create(processInstance);
      //设置流程id
      historicProcessInstance.setStartActivityId(startElement.getId());
      //通过engine配置中心配置过后，返回历史引擎管理
      this.getHistoricProcessInstanceEntityManager().insert(historicProcessInstance, false);
      //activiti事件分派器
      ActivitiEventDispatcher activitiEventDispatcher = this.getEventDispatcher();
      if (activitiEventDispatcher != null && activitiEventDispatcher.isEnabled()) {
        //将给定事件分派给已注册的任何侦听器  说明创建了一个流程。
        activitiEventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(
            ActivitiEventType.HISTORIC_PROCESS_INSTANCE_CREATED, historicProcessInstance));
      }
    }

  }

  public void recordSubProcessInstanceStart(ExecutionEntity parentExecution,
      ExecutionEntity subProcessInstance, FlowElement initialElement) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricProcessInstanceEntity historicProcessInstance = this.getHistoricProcessInstanceEntityManager()
          .create(subProcessInstance);
      if (historicProcessInstance.getStartActivityId() == null) {
        historicProcessInstance.setStartActivityId(initialElement.getId());
      }

      this.getHistoricProcessInstanceEntityManager().insert(historicProcessInstance, false);
      ActivitiEventDispatcher activitiEventDispatcher = this.getEventDispatcher();
      if (activitiEventDispatcher != null && activitiEventDispatcher.isEnabled()) {
        activitiEventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(
            ActivitiEventType.HISTORIC_PROCESS_INSTANCE_CREATED, historicProcessInstance));
      }

      HistoricActivityInstanceEntity activitiyInstance = this.findActivityInstance(parentExecution,
          false, true);
      if (activitiyInstance != null) {
        activitiyInstance.setCalledProcessInstanceId(subProcessInstance.getProcessInstanceId());
      }
    }

  }

  public void recordActivityStart(ExecutionEntity executionEntity) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY) && executionEntity.getActivityId() != null
        && executionEntity.getCurrentFlowElement() != null) {
      HistoricActivityInstanceEntity historicActivityInstanceEntity = null;
      HistoricActivityInstanceEntity historicActivityInstanceEntityFromCache = this.getHistoricActivityInstanceFromCache(
          executionEntity.getId(), executionEntity.getActivityId(), true);
      if (historicActivityInstanceEntityFromCache != null) {
        historicActivityInstanceEntity = historicActivityInstanceEntityFromCache;
      } else {
        historicActivityInstanceEntity = this.createHistoricActivityInstanceEntity(executionEntity);
      }

      ActivitiEventDispatcher activitiEventDispatcher = this.getEventDispatcher();
      if (activitiEventDispatcher != null && activitiEventDispatcher.isEnabled()) {
        activitiEventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(
            ActivitiEventType.HISTORIC_ACTIVITY_INSTANCE_CREATED, historicActivityInstanceEntity));
      }
    }

  }

  public void recordActivityEnd(ExecutionEntity executionEntity, String deleteReason) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricActivityInstanceEntity historicActivityInstance = this.findActivityInstance(
          executionEntity, false, true);
      if (historicActivityInstance != null) {
        historicActivityInstance.markEnded(deleteReason);
        ActivitiEventDispatcher activitiEventDispatcher = this.getEventDispatcher();
        if (activitiEventDispatcher != null && activitiEventDispatcher.isEnabled()) {
          activitiEventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(
              ActivitiEventType.HISTORIC_ACTIVITY_INSTANCE_ENDED, historicActivityInstance));
        }
      }
    }

  }

  public HistoricActivityInstanceEntity findActivityInstance(ExecutionEntity execution,
      boolean createOnNotFound, boolean endTimeMustBeNull) {
    String activityId = null;
    if (execution.getCurrentFlowElement() instanceof FlowNode) {
      activityId = execution.getCurrentFlowElement().getId();
    } else if (execution.getCurrentFlowElement() instanceof SequenceFlow
        && execution.getCurrentActivitiListener() == null) {
      activityId = ((SequenceFlow) execution.getCurrentFlowElement()).getSourceFlowElement()
          .getId();
    }

    return activityId != null ? this.findActivityInstance(execution, activityId, createOnNotFound,
        endTimeMustBeNull) : null;
  }

  public HistoricActivityInstanceEntity findActivityInstance(ExecutionEntity execution,
      String activityId, boolean createOnNotFound, boolean endTimeMustBeNull) {
    if (activityId == null) {
      return null;
    } else {
      String executionId = execution.getId();
      HistoricActivityInstanceEntity historicActivityInstanceEntityFromCache = this.getHistoricActivityInstanceFromCache(
          executionId, activityId, endTimeMustBeNull);
      if (historicActivityInstanceEntityFromCache != null) {
        return historicActivityInstanceEntityFromCache;
      } else {
        if (!execution.isInserted() && !execution.isProcessInstanceType()) {
          List<HistoricActivityInstanceEntity> historicActivityInstances = this.getHistoricActivityInstanceEntityManager()
              .findUnfinishedHistoricActivityInstancesByExecutionAndActivityId(executionId,
                  activityId);
          if (historicActivityInstances.size() > 0) {
            return (HistoricActivityInstanceEntity) historicActivityInstances.get(0);
          }
        }

        if (execution.getParentId() != null) {
          HistoricActivityInstanceEntity historicActivityInstanceFromParent = this.findActivityInstance(
              execution.getParent(), activityId, false, endTimeMustBeNull);
          if (historicActivityInstanceFromParent != null) {
            return historicActivityInstanceFromParent;
          }
        }

        return !createOnNotFound || activityId == null || (execution.getCurrentFlowElement() == null
            || !(execution.getCurrentFlowElement() instanceof FlowNode))
            && execution.getCurrentFlowElement() != null ? null
            : this.createHistoricActivityInstanceEntity(execution);
      }
    }
  }

  protected HistoricActivityInstanceEntity getHistoricActivityInstanceFromCache(String executionId,
      String activityId, boolean endTimeMustBeNull) {
    List<HistoricActivityInstanceEntity> cachedHistoricActivityInstances = this.getEntityCache()
        .findInCache(HistoricActivityInstanceEntity.class);
    Iterator var5 = cachedHistoricActivityInstances.iterator();

    HistoricActivityInstanceEntity cachedHistoricActivityInstance;
    do {
      do {
        do {
          do {
            if (!var5.hasNext()) {
              return null;
            }

            cachedHistoricActivityInstance = (HistoricActivityInstanceEntity) var5.next();
          } while (activityId == null);
        } while (!activityId.equals(cachedHistoricActivityInstance.getActivityId()));
      } while (endTimeMustBeNull && cachedHistoricActivityInstance.getEndTime() != null);
    } while (!executionId.equals(cachedHistoricActivityInstance.getExecutionId()));

    return cachedHistoricActivityInstance;
  }

  protected HistoricActivityInstanceEntity createHistoricActivityInstanceEntity(
      ExecutionEntity execution) {
    IdGenerator idGenerator = this.getProcessEngineConfiguration().getIdGenerator();
    String processDefinitionId = execution.getProcessDefinitionId();
    String processInstanceId = execution.getProcessInstanceId();
    HistoricActivityInstanceEntity historicActivityInstance = (HistoricActivityInstanceEntity) this.getHistoricActivityInstanceEntityManager()
        .create();
    historicActivityInstance.setId(idGenerator.getNextId());
    historicActivityInstance.setProcessDefinitionId(processDefinitionId);
    historicActivityInstance.setProcessInstanceId(processInstanceId);
    historicActivityInstance.setExecutionId(execution.getId());
    historicActivityInstance.setActivityId(execution.getActivityId());
    if (execution.getCurrentFlowElement() != null) {
      historicActivityInstance.setActivityName(execution.getCurrentFlowElement().getName());
      historicActivityInstance.setActivityType(
          this.parseActivityType(execution.getCurrentFlowElement()));
    }

    Date now = this.getClock().getCurrentTime();
    historicActivityInstance.setStartTime(now);
    if (execution.getTenantId() != null) {
      historicActivityInstance.setTenantId(execution.getTenantId());
    }

    this.getHistoricActivityInstanceEntityManager().insert(historicActivityInstance);
    return historicActivityInstance;
  }

  public void recordProcessDefinitionChange(String processInstanceId, String processDefinitionId) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricProcessInstanceEntity historicProcessInstance = (HistoricProcessInstanceEntity) this.getHistoricProcessInstanceEntityManager()
          .findById(processInstanceId);
      if (historicProcessInstance != null) {
        historicProcessInstance.setProcessDefinitionId(processDefinitionId);
      }
    }

  }

  public void recordTaskCreated(TaskEntity task, ExecutionEntity execution) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = this.getHistoricTaskInstanceEntityManager()
          .create(task, execution);
      this.getHistoricTaskInstanceEntityManager().insert(historicTaskInstance, false);
    }

    this.recordTaskId(task);
  }

  public void recordTaskAssignment(TaskEntity task) {
    ExecutionEntity executionEntity = task.getExecution();
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY) && executionEntity != null) {
      HistoricActivityInstanceEntity historicActivityInstance = this.findActivityInstance(
          executionEntity, false, true);
      if (historicActivityInstance != null) {
        historicActivityInstance.setAssignee(task.getAssignee());
      }
    }

  }

  public void recordTaskClaim(TaskEntity task) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(task.getId());
      if (historicTaskInstance != null) {
        historicTaskInstance.setClaimTime(task.getClaimTime());
      }
    }

  }

  public void recordTaskId(TaskEntity task) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      ExecutionEntity execution = task.getExecution();
      if (execution != null) {
        HistoricActivityInstanceEntity historicActivityInstance = this.findActivityInstance(
            execution, false, true);
        if (historicActivityInstance != null) {
          historicActivityInstance.setTaskId(task.getId());
        }
      }
    }

  }

  public void recordTaskEnd(String taskId, String deleteReason) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.markEnded(deleteReason);
      }
    }

  }

  public void recordTaskAssigneeChange(String taskId, String assignee) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setAssignee(assignee);
      }
    }

  }

  public void recordTaskOwnerChange(String taskId, String owner) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setOwner(owner);
      }
    }

  }

  public void recordTaskNameChange(String taskId, String taskName) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setName(taskName);
      }
    }

  }

  public void recordTaskDescriptionChange(String taskId, String description) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setDescription(description);
      }
    }

  }

  public void recordTaskDueDateChange(String taskId, Date dueDate) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setDueDate(dueDate);
      }
    }

  }

  public void recordTaskPriorityChange(String taskId, int priority) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setPriority(priority);
      }
    }

  }

  public void recordTaskCategoryChange(String taskId, String category) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setCategory(category);
      }
    }

  }

  public void recordTaskFormKeyChange(String taskId, String formKey) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setFormKey(formKey);
      }
    }

  }

  public void recordTaskParentTaskIdChange(String taskId, String parentTaskId) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setParentTaskId(parentTaskId);
      }
    }

  }

  public void recordTaskExecutionIdChange(String taskId, String executionId) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setExecutionId(executionId);
      }
    }

  }

  public void recordTaskDefinitionKeyChange(String taskId, String taskDefinitionKey) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setTaskDefinitionKey(taskDefinitionKey);
      }
    }

  }

  public void recordTaskProcessDefinitionChange(String taskId, String processDefinitionId) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricTaskInstanceEntity historicTaskInstance = (HistoricTaskInstanceEntity) this.getHistoricTaskInstanceEntityManager()
          .findById(taskId);
      if (historicTaskInstance != null) {
        historicTaskInstance.setProcessDefinitionId(processDefinitionId);
      }
    }

  }

  public void recordVariableCreate(VariableInstanceEntity variable) {
    //log.info("[MYLOG]:-----MyDefaultHistoryManager/recordVariableCreate==>processEngineConfiguration:[MyDefaultHistoryManager]");
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      this.getHistoricVariableInstanceEntityManager().copyAndInsert(variable);
    }

  }

  public void recordHistoricDetailVariableCreate(VariableInstanceEntity variable,
      ExecutionEntity sourceActivityExecution, boolean useActivityId) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.FULL)) {
      HistoricDetailVariableInstanceUpdateEntity historicVariableUpdate = this.getHistoricDetailEntityManager()
          .copyAndInsertHistoricDetailVariableInstanceUpdateEntity(variable);
      if (useActivityId && sourceActivityExecution != null) {
        HistoricActivityInstanceEntity historicActivityInstance = this.findActivityInstance(
            sourceActivityExecution, false, false);
        if (historicActivityInstance != null) {
          historicVariableUpdate.setActivityInstanceId(historicActivityInstance.getId());
        }
      }
    }

  }

  public void recordVariableUpdate(VariableInstanceEntity variable) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricVariableInstanceEntity historicProcessVariable = (HistoricVariableInstanceEntity) this.getEntityCache()
          .findInCache(HistoricVariableInstanceEntity.class, variable.getId());
      if (historicProcessVariable == null) {
        historicProcessVariable = this.getHistoricVariableInstanceEntityManager()
            .findHistoricVariableInstanceByVariableInstanceId(variable.getId());
      }

      if (historicProcessVariable != null) {
        this.getHistoricVariableInstanceEntityManager()
            .copyVariableValue(historicProcessVariable, variable);
      } else {
        this.getHistoricVariableInstanceEntityManager().copyAndInsert(variable);
      }
    }

  }

  public void createIdentityLinkComment(String taskId, String userId, String groupId, String type,
      boolean create) {
    this.createIdentityLinkComment(taskId, userId, groupId, type, create, false);
  }

  public void createUserIdentityLinkComment(String taskId, String userId, String type,
      boolean create) {
    this.createIdentityLinkComment(taskId, userId, (String) null, type, create, false);
  }

  public void createGroupIdentityLinkComment(String taskId, String groupId, String type,
      boolean create) {
    this.createIdentityLinkComment(taskId, (String) null, groupId, type, create, false);
  }

  public void createUserIdentityLinkComment(String taskId, String userId, String type,
      boolean create, boolean forceNullUserId) {
    this.createIdentityLinkComment(taskId, userId, (String) null, type, create, forceNullUserId);
  }

  public void createIdentityLinkComment(String taskId, String userId, String groupId, String type,
      boolean create, boolean forceNullUserId) {
    if (this.isHistoryEnabled()) {
      String authenticatedUserId = Authentication.getAuthenticatedUserId();
      CommentEntity comment = (CommentEntity) this.getCommentEntityManager().create();
      comment.setUserId(authenticatedUserId);
      comment.setType("event");
      comment.setTime(this.getClock().getCurrentTime());
      comment.setTaskId(taskId);
      if (userId == null && !forceNullUserId) {
        if (create) {
          comment.setAction("AddGroupLink");
        } else {
          comment.setAction("DeleteGroupLink");
        }

        comment.setMessage(new String[]{groupId, type});
      } else {
        if (create) {
          comment.setAction("AddUserLink");
        } else {
          comment.setAction("DeleteUserLink");
        }

        comment.setMessage(new String[]{userId, type});
      }

      this.getCommentEntityManager().insert(comment);
    }

  }

  public void createProcessInstanceIdentityLinkComment(String processInstanceId, String userId,
      String groupId, String type, boolean create) {
    this.createProcessInstanceIdentityLinkComment(processInstanceId, userId, groupId, type, create,
        false);
  }

  public void createProcessInstanceIdentityLinkComment(String processInstanceId, String userId,
      String groupId, String type, boolean create, boolean forceNullUserId) {
    if (this.isHistoryEnabled()) {
      String authenticatedUserId = Authentication.getAuthenticatedUserId();
      CommentEntity comment = (CommentEntity) this.getCommentEntityManager().create();
      comment.setUserId(authenticatedUserId);
      comment.setType("event");
      comment.setTime(this.getClock().getCurrentTime());
      comment.setProcessInstanceId(processInstanceId);
      if (userId == null && !forceNullUserId) {
        if (create) {
          comment.setAction("AddGroupLink");
        } else {
          comment.setAction("DeleteGroupLink");
        }

        comment.setMessage(new String[]{groupId, type});
      } else {
        if (create) {
          comment.setAction("AddUserLink");
        } else {
          comment.setAction("DeleteUserLink");
        }

        comment.setMessage(new String[]{userId, type});
      }

      this.getCommentEntityManager().insert(comment);
    }

  }

  public void createAttachmentComment(String taskId, String processInstanceId,
      String attachmentName, boolean create) {
    if (this.isHistoryEnabled()) {
      String userId = Authentication.getAuthenticatedUserId();
      CommentEntity comment = (CommentEntity) this.getCommentEntityManager().create();
      comment.setUserId(userId);
      comment.setType("event");
      comment.setTime(this.getClock().getCurrentTime());
      comment.setTaskId(taskId);
      comment.setProcessInstanceId(processInstanceId);
      if (create) {
        comment.setAction("AddAttachment");
      } else {
        comment.setAction("DeleteAttachment");
      }

      comment.setMessage(attachmentName);
      this.getCommentEntityManager().insert(comment);
    }

  }

  public void recordIdentityLinkCreated(IdentityLinkEntity identityLink) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT) && (
        identityLink.getProcessInstanceId() != null || identityLink.getTaskId() != null)) {
      HistoricIdentityLinkEntity historicIdentityLinkEntity = (HistoricIdentityLinkEntity) this.getHistoricIdentityLinkEntityManager()
          .create();
      historicIdentityLinkEntity.setId(identityLink.getId());
      historicIdentityLinkEntity.setGroupId(identityLink.getGroupId());
      historicIdentityLinkEntity.setProcessInstanceId(identityLink.getProcessInstanceId());
      historicIdentityLinkEntity.setTaskId(identityLink.getTaskId());
      historicIdentityLinkEntity.setType(identityLink.getType());
      historicIdentityLinkEntity.setUserId(identityLink.getUserId());
      this.getHistoricIdentityLinkEntityManager().insert(historicIdentityLinkEntity, false);
    }

  }

  public void deleteHistoricIdentityLink(String id) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.AUDIT)) {
      this.getHistoricIdentityLinkEntityManager().delete(id);
    }

  }

  public void updateProcessBusinessKeyInHistory(ExecutionEntity processInstance) {
    if (this.isHistoryEnabled()) {
      if (log.isDebugEnabled()) {
        log.debug("updateProcessBusinessKeyInHistory : {}", processInstance.getId());
      }

      if (processInstance != null) {
        HistoricProcessInstanceEntity historicProcessInstance = (HistoricProcessInstanceEntity) this.getHistoricProcessInstanceEntityManager()
            .findById(processInstance.getId());
        if (historicProcessInstance != null) {
          historicProcessInstance.setBusinessKey(processInstance.getProcessInstanceBusinessKey());
          this.getHistoricProcessInstanceEntityManager().update(historicProcessInstance, false);
        }
      }
    }

  }

  public void recordVariableRemoved(VariableInstanceEntity variable) {
    if (this.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY)) {
      HistoricVariableInstanceEntity historicProcessVariable = (HistoricVariableInstanceEntity) this.getEntityCache()
          .findInCache(HistoricVariableInstanceEntity.class, variable.getId());
      if (historicProcessVariable == null) {
        historicProcessVariable = this.getHistoricVariableInstanceEntityManager()
            .findHistoricVariableInstanceByVariableInstanceId(variable.getId());
      }

      if (historicProcessVariable != null) {
        this.getHistoricVariableInstanceEntityManager().delete(historicProcessVariable);
      }
    }

  }

  protected String parseActivityType(FlowElement element) {
    String elementType = element.getClass().getSimpleName();
    elementType = elementType.substring(0, 1).toLowerCase() + elementType.substring(1);
    return elementType;
  }

  protected EntityCache getEntityCache() {
    return (EntityCache) this.getSession(EntityCache.class);
  }

  public HistoryLevel getHistoryLevel() {
    return this.historyLevel;
  }

  public void setHistoryLevel(HistoryLevel historyLevel) {
    this.historyLevel = historyLevel;
  }
}
